package org.moonlight.jvm.rtda.share.heap;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.moonlight.jvm.classfile.ClassFile;
import org.moonlight.jvm.classfile.attribute.debuginfo.SourceFileAttribute;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.Thread;
import org.moonlight.jvm.rtda.share.Object;
import org.moonlight.jvm.rtda.share.heap.classmember.AccessFlags;
import org.moonlight.jvm.rtda.share.heap.classmember.Field;
import org.moonlight.jvm.rtda.share.heap.classmember.Method;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * 类对象{@link java.lang.Class}:
 *   类对象是会被放入到堆区的，在运行时通过该对象来描述一个类。平时java代码的 .class getClass() 都是指这个存储在堆区的类对象
 *   在虚拟机层面 {@link org.moonlight.jvm.classfile.ClassFile} 可以认为是这个类的元信息
 *   而在 java程序层面， {@link Class} 代表的才是这个类的元信息
 *
 * 通过 {@link org.moonlight.jvm.classpath.Classpath} 读取到 class文件的字节数据之后，
 * 再通过 {@link org.moonlight.jvm.classfile.ClassReader} 解析 class 文件，
 * 进而得到 {@link org.moonlight.jvm.classfile.ClassFile}
 * 然后对 {@link org.moonlight.jvm.classfile.ClassFile} 进行解析得到 {@link Class}
 * 而这个 {@link Class} 就是将要放进方法区内的类的元信息
 *
 * 类初始化就是执行类的初始化方法（<clinit>）。类的初始化在下列情况下触发:
 *   1. 执行new指令创建类实例，但类还没有被初始化。
 *   2. 执行putstatic、getstatic指令存取类的静态变量，但声明该字段的类还没有被初始化。
 *   3. 执行invokestatic调用类的静态方法，但声明该方法的类还没有被初始化。
 *   4. 当初始化一个类时，如果类的超类还没有被初始化，要先初始化类的超类
 *   5. 执行某些反射操作时
 * 在当前类中我们通过 {@link this#initStarted} 字段来判断类是否已经初始化
 *
 * @author Moonlight
 * @createTime 2023/8/29 15:50
 **/
@Getter
@EqualsAndHashCode
public class Class implements Serializable {

    /** 类型 与 描述符的映射 Map **/
    public static final Map<String, String> primitiveClassDescMapping;

    static {
        primitiveClassDescMapping = new HashMap<>(16);
        primitiveClassDescMapping.put("void", "V");
        primitiveClassDescMapping.put("boolean", "Z");
        primitiveClassDescMapping.put("byte", "B");
        primitiveClassDescMapping.put("short", "S");
        primitiveClassDescMapping.put("int", "I");
        primitiveClassDescMapping.put("long", "J");
        primitiveClassDescMapping.put("char", "C");
        primitiveClassDescMapping.put("float", "F");
        primitiveClassDescMapping.put("double", "D");
    }

    /** 访问标识符 **/
    private final int accessFlags;
    /** 这个类的类名 - 完全限定名, 如: java/lang/Object **/
    private final String name;
    /** 这个类的父类的类名 - 完全限定名, 如: java/lang/Object **/
    private String superClassName;
    /** 这个类的所实现的接口的接口名 - 完全限定名, 如: java/lang/Object **/
    private String[] interfaceNames;
    /** 运行时常量池 **/
    private RtConstantPool rtCp;
    /** 字段列表 **/
    private Field[] fields;
    /** 方法列表 **/
    private Method[] methods;
    /** 类加载器 **/
    @Setter
    private ClassLoader loader;
    /** 父类类信息 **/
    @Setter
    private Class superClass;
    /** 实现的接口信息 **/
    @Setter
    private Class[] interfaces;
    /**
     * 实例变量数
     *
     * 成员变量: 类内、方法体外定义的变量称为成员变量。
     * Java中的成员变量分为两种：
     *   1. 没有 static修饰的，这些成员变量是对象中的成员，称为实例变量。
     *   2. 是有 static修饰的，称为类变量（静态变量）。
     * 静态变量（类变量）具备以下特点:
     *   1. 随着类的加载而加载
     *   2. 优先于对象存在
     *   3. 被所有对象所共享
     *   4. 可以直接被类名调用
     * 类变量和实例变量的区别是：
     *   1，存放位置。
     *      a. 类变量随着类的加载而存在于方法区中。
     *      b. 实例变量随着对象的建立而存在于堆内存中。
     *   2，生命周期:
     *      a. 类变量生命周期最长，随着类的消失而消失。
     *      b. 实例变量生命周期随着对象的消失而消失。
     *  **/
    @Setter
    private int instanceSlotCount;
    /**
     * 类变量数
     *
     * 成员变量: 类内、方法体外定义的变量称为成员变量。
     * Java中的成员变量分为两种：
     *   1. 没有 static修饰的，这些成员变量是对象中的成员，称为实例变量。
     *   2. 是有 static修饰的，称为类变量（静态变量）。
     * 静态变量（类变量）具备以下特点:
     *   1. 随着类的加载而加载
     *   2. 优先于对象存在
     *   3. 被所有对象所共享
     *   4. 可以直接被类名调用
     * 类变量和实例变量的区别是：
     *   1，存放位置。
     *      a. 类变量随着类的加载而存在于方法区中。
     *      b. 实例变量随着对象的建立而存在于堆内存中。
     *   2，生命周期:
     *      a. 类变量生命周期最长，随着类的消失而消失。
     *      b. 实例变量生命周期随着对象的消失而消失。
     *  **/
    @Setter
    private int staticSlotCount;
    /** 存储 静态变量  **/
    @Setter
    private Slots staticVars;
    /** 类是否已经初始化 **/
    private boolean initStarted;
    /** java.lang.Class 实例 **/
    @Setter
    private Object jClass;

    /** 源文件名 **/
    @Setter
    private String sourceFile;

    public Class(ClassFile classFile) {
        this.accessFlags = classFile.getAccessFlags();
        this.name = classFile.getClassName();
        this.superClassName = classFile.getSuperClassName();
        this.interfaceNames = classFile.interfaceNames();
        this.rtCp = new RtConstantPool(this, classFile.getScp());
        this.fields = Field.newFields(this, classFile.getFields());
        this.methods = Method.newMethods(this, classFile.getMethods());
        this.sourceFile = getSourceFile(classFile);
    }

    public Class(int accessFlags, String className, ClassLoader loader, boolean initStarted, Class superClass, Class[] interfaces) {
        this.accessFlags = accessFlags;
        this.name = className;
        this.loader = loader;
        this.initStarted = initStarted;
        this.superClass = superClass;
        this.interfaces = interfaces;
    }

    /**
     * 设置类初始化标记为true，表示这个类已经经过类初始化了
     *
     * @createTime 17:10 2023/9/12
     * @author moonlight
     **/
    public void startInit() {
        this.initStarted = true;
    }

    /**
     * 判断 传入的类对象(clazz) 是否能够访问 当前类对象(this)
     *
     * @param clazz {@link Class} 类对象
     * @return boolean true 表示 传入的类对象(clazz) 能够访问 当前类对象(this)，反之为 false
     * @createTime 11:24 2023/8/30
     * @author moonlight
     **/
    public boolean isAccessibleTo(Class clazz) {
        return this.isPublic() || this.getPackageName().equals(clazz.getPackageName());
    }

    /**
     * 获取 当前类对象所在包的包名.
     * 从 {@link this#name} 切割出来包名, 比如: java/lang/Object 它的包名就是 java/lang
     * 如果类定义在默认包中，它的包名是空字符串。
     *
     * @return String 包名
     * @createTime 11:26 2023/8/30
     * @author moonlight
     **/
    public String getPackageName() {
        int i = this.name.lastIndexOf("/");
        if (i >= 0) {
            return this.name.substring(0, i);
        }
        return "";
    }

    /**
     * 判断 this 是否为 other 的子类
     *
     * @param other {@link Class} 类对象
     * @return boolean true 是 other 的子类，反之 不是
     * @createTime 16:00 2023/8/30
     * @author moonlight
     **/
    public boolean isSubClassOf(Class other) {
        for (Class c = this.superClass; c != null; c = c.superClass) {
            if (superClass.equals(other)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断 this 是否为 other 的父类
     *
     * @param other {@link Class} 类对象
     * @return boolean true 是 other 的父类，反之 不是
     * @createTime 15:10 2023/9/12
     * @author moonlight
     **/
    public boolean isSuperClassOf(Class other) {
        return other.isSubClassOf(this);
    }

    /**
     * 获取 main方法
     *
     * @return Method {@link Method} 方法对象
     * @createTime 16:00 2023/8/30
     * @author moonlight
     **/
    public Method getMainMethod() {
        return this.getMethod("main", "([Ljava/lang/String;)V", true);
    }

    /**
     * 根据 方法名字 和 方法描述符 查找 方法对象
     *
     * @param name 方法名字
     * @param desc 方法描述符
     * @return Method {@link Method} 方法对象
     * @createTime 16:01 2023/8/30
     * @author moonlight
     **/
    private Method getMethod(String name, String desc, boolean isStatic) {
        for (Method m : this.getMethods()) {
            if (m.isStatic() == isStatic && name.equals(m.getName()) && desc.equals(m.getDescriptor())) {
                return m;
            }
        }
        return null;
    }

    /**
     * 创建一个全新的{@link Object}对象
     *
     * @return Object {@link Object}
     * @createTime 16:03 2023/8/30
     * @author moonlight
     **/
    public Object newObject() {
        return new Object(this);
    }

    /**
     * 判断当前类对象是否为数组类对象，如果当前类对象的名字以 [ 开头那么说明他是个数组类
     * @return boolean true 数组类 false 非数组类
     * @createTime 10:33 2023/9/13
     * @author moonlight
     **/
    public boolean isArray() {
        return this.name.charAt(0) == '[';
    }

    /**
     * 创建数组对象
     *
     * @param count 数组长度
     * @return Object {@link Object}
     * @createTime 10:40 2023/9/13 
     * @author moonlight
     **/
    public Object newArray(int count) {
        if (!this.isArray()) {
            throw new RuntimeException("Not array class " + this.name);
        }
        switch (this.name) {
            case "[Z":
            case "[B":
                // 布尔数组是通过字节数组来表示的
                return new Object(this, new byte[count]);
            case "[C":
                return new Object(this, new char[count]);
            case "[S":
                return new Object(this, new short[count]);
            case "[I":
                return new Object(this, new int[count]);
            case "[J":
                return new Object(this, new long[count]);
            case "[F":
                return new Object(this, new float[count]);
            case "[D":
                return new Object(this, new double[count]);
            default:
                return new Object(this, new Object[count]);
        }
    }

    /**
     * 获取与类对应的数组类
     *
     * @return 类对应的数组类
     * @createTime 14:19 2023/9/13
     * @author moonlight
     **/
    public Class arrayClass() {
       String arrClassName = getArrayClassName();
       return this.loader.loadClass(arrClassName);
    }

    /**
     * 获取数组类的类名
     * @return 数组类的类名
     * @createTime 14:21 2023/9/13
     * @author moonlight
     **/
    private String getArrayClassName() {
        return "[" + toDescriptor();
    }

    /**
     * 将类名转换为类型描述符
     * @return 类型描述符
     * @createTime 14:22 2023/9/13
     * @author moonlight
     **/
    private String toDescriptor() {
        if (this.name.charAt(0) == '[') {
            return this.name;
        }
        if (primitiveClassDescMapping.containsKey(this.name)) {
            return primitiveClassDescMapping.get(this.name);
        }
        return "L" + this.name + ";";
    }

    /**
     * 判断 other类型 的 引用值，是否可以赋值给 this类型。在三种情况下，other类型 引用值 可以赋值给 this类型的:
     *   1. other 和 this 是 同一类型
     *   2. other 是 this 的子类
     *   3. other 是 this 的实现类
     *
     * @param other {@link Class} 类对象
     * @return boolean true other类型 的 引用值，可以赋值给 this类型. 反之不可以
     * @createTime 16:06 2023/8/30
     * @author moonlight
     **/
    public boolean isAssignableFrom(Class other) {
        if (this.equals(other)) {
            return true;
        }
        if (!other.isArray()) {
            // other 不是数组
            if (!other.isInterface()) {
                // other 是 class
                if (!this.isInterface()) {
                    // this 不是接口
                    return other.isSubClassOf(this);
                } else {
                    // this 是接口
                    return other.isImplements(this);
                }
            } else {
                // s 是接口
                if (!this.isInterface()) {
                    // this 不是接口
                    return this.isJlObject();
                } else {
                    // this 是接口
                    return this.isSuperInterfaceOf(other);
                }
            }
        } else {
            // other 是数组
            if (!this.isArray()) {
                // this 不是数组
                if (!this.isInterface()) {
                    // this 是 class
                    return this.isJlObject();
                } else {
                    // this 是接口
                    return this.isJlCloneable() || this.isJioSerializable();
                }
            } else {
                // this 是数组
                Class sc = other.componentClass();
                Class tc = this.componentClass();
                return sc == tc || tc.isAssignableFrom(sc);
            }
        }
    }

    private boolean isJlObject() {
        return "java/lang/Object".equals(this.name);
    }

    private boolean isJlCloneable() {
        return "java/lang/Cloneable".equals(this.name);
    }

    private boolean isJioSerializable() {
        return "java/io/Serializable".equals(this.name);
    }

    /**
     * 判断 this 是否实现了 otherInterface这个接口，或者 this 是不是 otherInterface 的子接口
     *
     * @param otherInterface {@link Class} 类对象，不过这是一个接口
     * @return boolean true this 实现了 otherInterface 这个接口，或者它是这个接口的子接口 反之 没有实现也不是它的子接口
     * @createTime 16:20 2023/8/30
     * @author moonlight
     **/
    public boolean isImplements(Class otherInterface) {
        for (Class c = this; c != null; c = c.superClass) {
            for (Class inter : c.interfaces) {
                if (inter.equals(otherInterface) || inter.isSubInterfaceOf(otherInterface)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断 this 是否是 interFace的子接口
     *
     * @param interFace {@link Class} 类对象，不过这是一个接口
     * @return true this 是 interFace的子接口，反之不是
     * @createTime 16:24 2023/8/30
     * @author moonlight
     **/
    private boolean isSubInterfaceOf(Class interFace) {
        if (this.interfaces != null) {
            for (Class c : this.interfaces) {
                if (c.equals(interFace) || c.isSubInterfaceOf(interFace)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断 this 是否是 interFace的父接口
     *
     * @param interFace {@link Class} 类对象，不过这是一个接口
     * @return true this 是 interFace的父接口，反之不是
     * @createTime 15:10 2023/9/12
     * @author moonlight
     **/
    public boolean isSuperInterfaceOf(Class interFace) {
        return interFace.isSubInterfaceOf(this);
    }

    public boolean isPublic() {
        return (this.accessFlags & AccessFlags.ACC_PUBLIC) != 0;
    }

    public boolean isFinal() {
        return (this.accessFlags & AccessFlags.ACC_FINAL) != 0;
    }

    public boolean isSuper() {
        return (this.accessFlags & AccessFlags.ACC_SUPER) != 0;
    }

    public boolean isInterface() {
        return (this.accessFlags & AccessFlags.ACC_INTERFACE) != 0;
    }

    public boolean isAbstract() {
        return (this.accessFlags & AccessFlags.ACC_ABSTRACT) != 0;
    }

    public boolean isSynthetic() {
        return (this.accessFlags & AccessFlags.ACC_SYNTHETIC) != 0;
    }

    public boolean isAnnotation() {
        return (this.accessFlags & AccessFlags.ACC_ANNOTATION) != 0;
    }

    public boolean isEnum() {
        return (this.accessFlags & AccessFlags.ACC_ENUM) != 0;
    }

    /**
     * 类的初始化
     * @param thread 正在执行中的虚拟机线程对象{@link Thread}
     * @createTime 9:02 2023/9/13
     * @author moonlight
     **/
    public void classInit(Thread thread) {
        // 将 initStarted 设置为 true，标识已经类已经初始化
        this.startInit();
        this.scheduleClinit(thread);
        // 如果 父类还没有经过类初始化，那么这里可以保证父类类初始化方法的栈帧压在子类类初始化方法栈帧的上面
        this.initSuperClass(thread);
    }

    /**
     * 先执行父类的类初始化
     *
     * @param thread 正在执行中的虚拟机线程对象{@link Thread}
     * @createTime 9:30 2023/9/13
     * @author moonlight
     **/
    private void initSuperClass(Thread thread) {
        if (!this.isInterface()) {
            if (this.superClass != null && !this.superClass.isInitStarted()) {
                this.superClass.classInit(thread);
            }
        }
    }

    /**
     * 执行类初始化，注意: 可能需要先执行父类的类初始化, 如果父类没有经过类初始化的话
     *
     * @param thread 正在执行中的虚拟机线程对象{@link Thread}
     * @createTime 9:18 2023/9/13 
     * @author moonlight
     **/
    private void scheduleClinit(Thread thread) {
        Method clinit = this.getClinit();
        if (clinit != null) {
//            System.out.println(this.getName() + "的初始化方法: " + new Gson().toJson(clinit));
            Frame frame = thread.newFrame(clinit);
            thread.pushFrame(frame);
        }
    }

    /**
     * 获取 类初始化方法，这个方法是由编译器按照 从上到下 的顺序
     * 收集 类中的静态字段，静态代码块，组织成的，执行时，如果父类
     * 还没有执行类初始化，那就先执行父类的类初始化，再执行子类的。
     *
     * @return Method clinit - 类初始化方法
     * @createTime 9:07 2023/9/13
     * @author moonlight
     **/
    private Method getClinit() {
        return this.getMethod("<clinit>", "()V", true);
    }

    /**
     * 数组类的元素类型
     *  1. 先根据数组类名推测出数组元素类名
     *  2. 然后用类加载器加载元素类即可
     *
     * @return Class 数组类的元素类型
     * @createTime 16:18 2023/9/15
     * @author moonlight
     **/
    public Class componentClass() {
       String componentClassName = getComponentClassName();
       return this.loader.loadClass(componentClassName);
    }

    /**
     * 根据数组类名推测出数组元素类名
     *
     * @return String 数组元素类名
     * @createTime 16:22 2023/9/15
     * @author moonlight
     **/
    private String getComponentClassName() {
        if (this.name.charAt(0) == '[') {
           String componentTypeDesc = this.name.substring(1);
           return toClassName(componentTypeDesc);
        }
        throw new RuntimeException("not array: " + this.name);
    }

    /**
     * 根据 数组元素类描述符 转换出 数组元素类名
     *  1. 如果类型描述符以方括号开头，那么肯定是数组，描述符即是类名
     *  2. 如果类型描述符以L开头，那么肯定是类描述符，去掉开头的L和末尾的分号即是类名
     *  3. 否则判断是否是基本类型的描述符，如果是，返回基本类型名称
     *  4. 以上情况都不符合就直接抛异常
     *
     * @param componentTypeDesc 数组元素类描述符
     * @return 数组元素类名
     * @createTime 16:27 2023/9/15
     * @author moonlight
     **/
    private String toClassName(String componentTypeDesc) {
        // 如果类型描述符以方括号开头，那么肯定是数组，描述符即是类名
        if (componentTypeDesc.charAt(0) == '[') {
            return componentTypeDesc;
        }
        // 如果类型描述符以L开头，那么肯定是类描述符，去掉开头的L和末尾的分号即是类名
        if (componentTypeDesc.charAt(0) == 'L') {
            return componentTypeDesc.substring(1, componentTypeDesc.length() - 1);
        }
        // 否则判断是否是基本类型的描述符，如果是，返回基本类型名称
        for (Map.Entry<String, String> entry : primitiveClassDescMapping.entrySet()) {
            if (entry.getValue().equals(componentTypeDesc)) {
                return entry.getKey();
            }
        }
        throw new RuntimeException("invalid descriptor: " + componentTypeDesc);
    }


    public Field getField(String name, String descriptor, boolean isStatic) {
        for (Class c = this; c != null; c = c.superClass) {
            for (Field f : c.getFields()) {
                if (f.isStatic() == isStatic && f.getName().equals(name) && f.getDescriptor().equals(descriptor)) {
                    return f;
                }
            }
        }
        return null;
    }

    public Field getField(String name, String descriptor) {
        Field f;
        if ( (f = getField(name, descriptor, false)) == null) {
            f = getField(name, descriptor, true);
        }
        return f;
    }

    public Object getRefVar(String fieldName, String fieldDesc) {
        Field field = getField(fieldName, fieldDesc, true);
        return this.staticVars.getRef(field.getSlotId());
    }

    public void setRefVar(String fieldName, String fieldDesc, Object obj) {
        Field field = getField(fieldName, fieldDesc, true);
        this.staticVars.setRef(field.getSlotId(), obj);
    }

    public String getJavaName() {
        return this.name.replaceAll("/", ".");
    }

    public boolean isPrimitive() {
        return primitiveClassDescMapping.containsKey(this.name);
    }

    public Method getInstanceMethod(String methodName, String methodDesc) {
        return getMethod(methodName, methodDesc, false);
    }

    public String getSourceFile(ClassFile classFile) {
        SourceFileAttribute sourceFileAttribute = classFile.sourceFileAttribute();
        if (sourceFileAttribute != null) {
            return sourceFileAttribute.fileName();
        }
        return "Unknown";
    }

    public Method getStaticMethod(String methodName, String methodDesc) {
        return getMethod(methodName, methodDesc, true);
    }
}
